package com.lechi.web.controller;

import com.lechi.iot.netty.channel.ITcpChannel;
import com.lechi.iot.netty.utils.BytesToHexUtil;
import com.lechi.web.common.annotation.Anonymous;
import com.lechi.web.common.core.controller.BaseController;
import com.lechi.web.common.core.domain.AjaxResult;
import com.lechi.web.common.core.page.TableDataInfo;
import com.lechi.web.common.exception.ServiceException;
import com.lechi.web.constants.Constants;
import com.lechi.web.framework.util.GuavaCacheUtil;
import com.lechi.web.framework.util.JwtTokenUtil;
import com.lechi.web.service.IIotDeviceService;
import com.lechi.web.service.IIotMonitorDataService;
import com.lechi.web.system.domain.*;
import com.lechi.web.util.LeChiCacheUtil;
import com.lechi.web.vo.OutBoundDataQueryVo;
import com.lechi.web.vo.OutBoundDeviceQueryVo;
import com.lechi.web.vo.OutBoundHistoryQueryVo;
import io.jsonwebtoken.Claims;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.nio.charset.Charset;
import java.util.*;

/**
 * 监测数据Controller
 *
 * @author yangguang
 */
@Slf4j
@RestController
@RequestMapping("/out")
@RequiredArgsConstructor
@Anonymous
public class OutBoundDataController extends BaseController {

    private final IIotMonitorDataService iotMonitorDataService;

    private final IIotDeviceService iotDeviceService;

    /**
     * 获取传感器数据
     *
     * @param outBoundDataQueryVo
     * @return the data
     */
    @PostMapping("/latest")
    public TableDataInfo latest(@RequestBody @Valid OutBoundDataQueryVo outBoundDataQueryVo) {
        List<OutBondDeviceBo> iotDeviceBoList = queryDevice(outBoundDataQueryVo);
        if (Objects.isNull(iotDeviceBoList) || iotDeviceBoList.isEmpty()) {
            return getDataTable(Lists.newArrayList());
        }
        List<OutBondMonitorDataBo> iotMonitorDataBos = new ArrayList<>();
        for (OutBondDeviceBo iotDeviceBo : iotDeviceBoList) {
            IotMonitorDataBo iotMonitorDataBo = new IotMonitorDataBo();
            iotMonitorDataBo.setTableName(Constants.MONITOR_DATA_TABLE + iotDeviceBo.getId());
            List<OutBondMonitorDataBo> iotMonitorData = iotMonitorDataService.selectOutBoundLatestMonitorData(iotMonitorDataBo);
            iotMonitorDataBos.addAll(iotMonitorData);
        }
        return getDataTable(iotMonitorDataBos);
    }

    private List<OutBondDeviceBo> queryDevice(OutBoundDataQueryVo outBoundDataQueryVo) {
        List<String> list = outBoundDataQueryVo.getDeviceSN();
        String authorization = outBoundDataQueryVo.getAuthorization();
        authorization = authorization.replace("Bearer ", "");
        IotDeviceBo iotDeviceBo = new IotDeviceBo();
        iotDeviceBo.setDeviceSn(String.format("'%s'", String.join("','", list)));
        Claims claims = JwtTokenUtil.validateDynamicToken(authorization);
        if (claims == null) {
            throw new ServiceException("Invalid dynamic token");
        }
        iotDeviceBo.setDeviceStation(claims.getSubject());
        return iotDeviceService.outBoundSelectDeviceList(iotDeviceBo);
    }

    @PostMapping("/query")
    public TableDataInfo query(@RequestBody @Valid OutBoundHistoryQueryVo historyQueryVo) {
        OutBoundDataQueryVo outBoundDataQueryVo = new OutBoundDataQueryVo();
        outBoundDataQueryVo.setAuthorization(historyQueryVo.getAuthorization());
        outBoundDataQueryVo.setDeviceSN(List.of(historyQueryVo.getDeviceSN()));
        List<OutBondDeviceBo> iotDeviceBoList = queryDevice(outBoundDataQueryVo);
        if (Objects.isNull(iotDeviceBoList) || iotDeviceBoList.isEmpty()) {
            return getDataTable(Lists.newArrayList());
        }
        OutBondDeviceBo iotDeviceBo = iotDeviceBoList.get(0);
        IotMonitorDataBo iotMonitorDataBo = new IotMonitorDataBo();
        BeanUtils.copyProperties(outBoundDataQueryVo, iotMonitorDataBo);
        iotMonitorDataBo.setTableName(Constants.MONITOR_DATA_TABLE + iotDeviceBo.getId());
        int pageNum = Objects.isNull(historyQueryVo.getPageNum()) ? 1 : historyQueryVo.getPageNum();
        int pageSize =  Objects.isNull(historyQueryVo.getPageSize()) ? 1 : historyQueryVo.getPageSize();
        int offset = (pageNum - 1) * pageSize;
        Map<String, Object> params = new HashMap<>();
        params.put("beginTime", historyQueryVo.getBeginTime());
        params.put("endTime", historyQueryVo.getEndTime());
        params.put("pageSize", pageSize);
        params.put("offset", offset);
        iotMonitorDataBo.setParams(params);
        iotMonitorDataBo.setSensorAliasId(1242L);
        List<OutBondMonitorDataBo> list = iotMonitorDataService.selectOutBoundMonitorDataList(iotMonitorDataBo);
        return getDataTable(list);
    }

    @PostMapping("/device/query")
    public TableDataInfo deviceQuery(@RequestBody @Valid OutBoundDeviceQueryVo outBoundDataQueryVo) {
        IotDeviceBo iotDeviceBo = new IotDeviceBo();
        String authorization = outBoundDataQueryVo.getAuthorization();
        Claims claims = JwtTokenUtil.validateDynamicToken(authorization.replace("Bearer ", ""));
        if (claims == null) {
            throw new ServiceException("Invalid dynamic token");
        }
        iotDeviceBo.setDeviceStation(claims.getSubject());
        List<OutBondDeviceBo> iotDeviceBoList = iotDeviceService.outBoundSelectDeviceList(iotDeviceBo);
        List<OutBondDeviceResult> list = iotDeviceBoList.stream().map(iotDevice -> {
            OutBondDeviceResult outBondDeviceResult = new OutBondDeviceResult();
            BeanUtils.copyProperties(iotDevice, outBondDeviceResult);
            return outBondDeviceResult;
        }).toList();
        return getDataTable(list);
    }

    @PostMapping("/send")
    public AjaxResult send(@RequestBody @Valid OutBoundDataQueryVo outBoundDataQueryVo) {
        List<OutBondDeviceBo> result = queryDevice(outBoundDataQueryVo);
        if (Objects.isNull(result)) {
            return AjaxResult.success();
        }
        OutBondDeviceBo iotDevice = result.get(0);
        String hostName = GuavaCacheUtil.getBiDeviceSnAndIPMap().get(iotDevice.getDeviceSn());
        if (Constants.WEATHER2017.equals(iotDevice.getProtocolType()) || Constants.WEATHER2024.equals(iotDevice.getProtocolType())) {
            hostName = GuavaCacheUtil.getDeviceIdAndIPMap().get(iotDevice.getId());
        }
//        if (Objects.isNull(hostName) || Objects.equals(deviceSn, hostName)) {
//            throw new ServiceException("该设备id未注册，未找到对应设备地址！", 3001);
//        }

        ITcpChannel tcpChannel = LeChiCacheUtil.getTcpChannelMap().get(hostName);
        if (tcpChannel == null) {
            log.error("用户发送指令异常，通过设备地址：{} 获取tcpChannel值为空， 请联系工程师解决！", hostName);
            throw new ServiceException("该设备未找到对应tcpChannel,请重启设备再试！", 3002);
        }
        String cmd = "";
        try {
            if (Constants.MODBUS_RTU.equals(iotDevice.getProtocolType())) {
                byte[] sendMsg = BytesToHexUtil.hexStrToBinaryStr(cmd);
//                TcpMessage message = new TcpByteArrayMessage(sendMsg);
                ByteBuf byteBuf = Unpooled.wrappedBuffer(sendMsg);
                tcpChannel.send(byteBuf);
            }

            if (Constants.WEATHER2017.equals(iotDevice.getProtocolType()) || Constants.WEATHER2024.equals(iotDevice.getProtocolType())) {
                log.error("用户发送指令:{}，通过设备地址：{}", cmd, hostName);
                ByteBuf message = ByteBufAllocator.DEFAULT.buffer();
                message.writeCharSequence(cmd, Charset.defaultCharset());
                tcpChannel.send(message);
            }
        } catch (Exception e) {
            log.error("用户发送16进制指令异常{} ，请联系工程师解决！", cmd);
            throw new ServiceException("16进制指令解析异常，错误提示：" + e.getMessage(), 3003);
        }
        return AjaxResult.success();
    }
}
